package org.moyrax.nereyda.services.core
{
	import org.moyrax.nereyda.lib.DateISO;
	
	import mx.collections.ArrayCollection;
	
	public class Parameter
	{
		public static const SERIAL_OBJECT :int = 0;
		public static const SERIAL_ARRAY  :int = 1;

		/**
		 * Determina si se forzará a que todos los tipos
		 * de parámetros se conviertan en String.
		 */
		public  var forceString :Boolean = true;
		
		private var params : ArrayCollection = new ArrayCollection();
		
		/**
		 * Devuelve la cantidad de parámetros que
		 * posee este objeto Parameter.
		 */
		public function get length():int
		{
			return( this.params.length );
		}
		
		/**
		 * Devuelve el valor del elemento que
		 * corresponde al índice especificado.
		 */
		public function getItem( index:int ):Object
		{
			return( this.params.getItemAt( index ).paramValue );
		}

		/**
		 * Devuelve el nombre del elemento que
		 * corresponde al índice especificado.
		 */
		public function getItemName( index:int ):String
		{
			return( this.params.getItemAt( index ).paramName );
		}
		
		/**
		 * Convierte este objeto en un String con
		 * formato para ser enviado al servidor.
		 */
		 public function serialize( format:int = SERIAL_OBJECT, userFormat:Boolean = false):String
		 {
		 	var result : String = ( format == SERIAL_OBJECT ) ? "{\n" : "[\n";
		 	
		 	for each ( var param:Object in this.params ) {
		 		if ( format == SERIAL_OBJECT ) {
		 			result += "\t" + param.paramName + ":" + param.paramValue + ",\n";
		 		}
		 		else {
		 			result += param.paramValue + ",\n";
		 		}
		 	}
		 	
		 	result  = result.substr(0, result.length - 2) + "\n";
		 	result += ( format == SERIAL_OBJECT ) ? "}\n" : "]\n";
		 	
		 	if ( !userFormat ) {
		 		result = result.replace(/[\n\t]/ig, "");
		 	}

		 	return( result );
		 }
		 
		/**
		 * Agrega un nuevo parámetro y reconoce
		 * el tipo de datos del mismo.
		 * 
		 * @param name  Nombre del parámetro.
		 * @param value Valor del parámetro.
		 */
		public function addParam( name:String, value:Object ):void
		{
			if ( value is String ) {
				this.addParamStr ( name, value as String );
			}
			else if ( value is Number ) {
				this.addParamNum ( name, value as Number );
			}
			else if ( value is Date ) {
				this.addParamDate( name, value as Date );
			}
			else if ( value is Array ) {
				this.addParamArray( name, value as Array );
			}
			else {
				this.addParamObj( name, value );
			}
		}
		
		/**
		 * Agrega un nuevo parámetro de tipo String.
		 * 
		 * @param name  Nombre del parámetro.
		 * @param value Valor del parámetro.
		 */
		public function addParamStr( name:String, value:String ):void
		{
			var clean : String = value.replace(/["']/ig, "\\\"");
			
			this.params.addItem( {
				paramName  : this.forceString ? "\"" + name + "\"" : name,
				paramValue : "\"" + clean + "\""
			} );
		}

		/**
		 * Agrega un nuevo parámetro de tipo Number.
		 * 
		 * @param name  Nombre del parámetro.
		 * @param value Valor del parámetro.
		 */
		public function addParamNum( name:String, value:Number ):void
		{
			var strValue :String = this.forceString ? 
				"\"" + value.toString() + "\"" :
				value.toString();
			
			this.params.addItem( {
				paramName  : this.forceString ? "\"" + name + "\"" : name,
				paramValue : strValue
			} );
		}

		/**
		 * Agrega un nuevo parámetro de tipo Date.
		 * 
		 * @param name  Nombre del parámetro.
		 * @param value Valor del parámetro.
		 */
		public function addParamDate( name:String, value:Date ):void
		{
			var date:DateISO = new DateISO(value);
			
			this.params.addItem( {
				paramName  : this.forceString ? "\"" + name + "\"" : name,
				paramValue : "\"" + date.toISO( DateISO.FORMAT_FULL_DATETIME ) + "\""
			} );
		}

		/**
		 * Agrega un nuevo parámetro de tipo Array.
		 * 
		 * @param name  Nombre del parámetro.
		 * @param value Valor del parámetro.
		 */
		public function addParamArray( name:String, value:Array ):void
		{
			var param : Parameter = new Parameter();
			
			param.forceString = this.forceString;
			
			for ( var i:int = 0; i < value.length; i++ ) {
				param.addParam( null, value[i] );
			}
			
			this.params.addItem( {
				paramName  : this.forceString ? "\"" + name + "\"" : name,
				paramValue : param.serialize( SERIAL_ARRAY )
			} );
		}

		/**
		 * Agrega un nuevo parámetro de tipo Object.
		 * 
		 * @param name  Nombre del parámetro.
		 * @param value Valor del parámetro.
		 */
		private function addParamObj( name:String, value:Object ):void
		{
			var param : Parameter = new Parameter();
			
			param.forceString = this.forceString;
			
			for ( var member : String in value ) {
				param.addParam( member, value[member] );
			}
			
			this.params.addItem( {
				paramName  : this.forceString ? "\"" + name + "\"" : name,
				paramValue : param.serialize( SERIAL_OBJECT )
			} );
		}
		
		public function Parameter(...params :Array ):void
		{
			for ( var i:int = 0; i < params.length; i+=2 ) {
				this.addParam(params[i].toString(), params[i + 1] );
			}
		}
	}
}